//! Data models for the AI Test Platform
//! 
//! This module contains all the core data structures used throughout the platform,
//! closely following the HttpRunner architecture while adding AI enhancements.

pub mod testcase;
pub mod config;
pub mod step;
pub mod request;
pub mod response;
pub mod summary;
pub mod plugin;
pub mod ai;
pub mod validation;

// Re-export commonly used types
pub use testcase::{ITestCase, TestCase, TestCaseDef, TestCasePath, TestCaseJson};
pub use config::{TestConfig, StepConfig, PluginConfig, ThinkTimeConfig, WebSocketConfig};
pub use step::{IStep, StepType, StepResult, ActionResult};
pub use request::{HttpRequest, HttpMethod, HttpResponse};
pub use summary::{TestSummary, TestCaseSummary, TestStat, TestTime, Platform};
pub use plugin::{PluginManifest, PluginInfo, PluginType};
pub use ai::{AIServiceOptions, AIModel, AIPrompt, AIResponse};
pub use validation::{Validator, ValidatorType, ValidationResult};

/// Common value type used throughout the platform
pub type Value = serde_json::Value;

/// Common map type for key-value pairs
pub type ValueMap = std::collections::HashMap<String, Value>;

/// Common string map type
pub type StringMap = std::collections::HashMap<String, String>;

/// Trait for types that can be converted to/from JSON
pub trait JsonSerializable: serde::Serialize + for<'de> serde::Deserialize<'de> {
    /// Convert to JSON string
    fn to_json(&self) -> crate::Result<String> {
        serde_json::to_string(self).map_err(|e| {
            crate::error::Error::Serialization(
                crate::error::SerializationError::JsonSerialization {
                    reason: e.to_string(),
                }
            )
        })
    }
    
    /// Convert to pretty JSON string
    fn to_json_pretty(&self) -> crate::Result<String> {
        serde_json::to_string_pretty(self).map_err(|e| {
            crate::error::Error::Serialization(
                crate::error::SerializationError::JsonSerialization {
                    reason: e.to_string(),
                }
            )
        })
    }
    
    /// Create from JSON string
    fn from_json(json: &str) -> crate::Result<Self> {
        serde_json::from_str(json).map_err(|e| {
            crate::error::Error::Serialization(
                crate::error::SerializationError::JsonSerialization {
                    reason: e.to_string(),
                }
            )
        })
    }
}

/// Trait for types that can be converted to/from YAML
pub trait YamlSerializable: serde::Serialize + for<'de> serde::Deserialize<'de> {
    /// Convert to YAML string
    fn to_yaml(&self) -> crate::Result<String> {
        serde_yaml::to_string(self).map_err(|e| {
            crate::error::Error::Serialization(
                crate::error::SerializationError::YamlSerialization {
                    reason: e.to_string(),
                }
            )
        })
    }
    
    /// Create from YAML string
    fn from_yaml(yaml: &str) -> crate::Result<Self> {
        serde_yaml::from_str(yaml).map_err(|e| {
            crate::error::Error::Serialization(
                crate::error::SerializationError::YamlSerialization {
                    reason: e.to_string(),
                }
            )
        })
    }
}

/// Automatically implement JsonSerializable for types that implement Serialize + Deserialize
impl<T> JsonSerializable for T where T: serde::Serialize + for<'de> serde::Deserialize<'de> {}

/// Automatically implement YamlSerializable for types that implement Serialize + Deserialize
impl<T> YamlSerializable for T where T: serde::Serialize + for<'de> serde::Deserialize<'de> {}

/// Trait for types that have a unique identifier
pub trait Identifiable {
    type Id;
    
    /// Get the unique identifier
    fn id(&self) -> &Self::Id;
}

/// Trait for types that have a name
pub trait Named {
    /// Get the name
    fn name(&self) -> &str;
}

/// Trait for types that can be validated
pub trait Validatable {
    /// Validate the object
    fn validate(&self) -> crate::Result<()>;
}

/// Trait for types that can be cloned deeply
pub trait DeepClone {
    /// Create a deep clone of the object
    fn deep_clone(&self) -> Self;
}

/// Common timestamp type
pub type Timestamp = chrono::DateTime<chrono::Utc>;

/// Helper function to get current timestamp
pub fn now() -> Timestamp {
    chrono::Utc::now()
}

/// Helper function to parse timestamp from string
pub fn parse_timestamp(s: &str) -> crate::Result<Timestamp> {
    chrono::DateTime::parse_from_rfc3339(s)
        .map(|dt| dt.with_timezone(&chrono::Utc))
        .map_err(|e| crate::error::internal_error(format!("Invalid timestamp format: {}", e)))
}

/// Helper function to format timestamp as string
pub fn format_timestamp(ts: &Timestamp) -> String {
    ts.to_rfc3339()
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde::{Deserialize, Serialize};

    #[derive(Serialize, Deserialize)]
    struct TestStruct {
        name: String,
        value: i32,
    }

    #[test]
    fn test_json_serializable() {
        let test_obj = TestStruct {
            name: "test".to_string(),
            value: 42,
        };
        
        let json = test_obj.to_json().unwrap();
        assert!(json.contains("test"));
        assert!(json.contains("42"));
        
        let parsed: TestStruct = TestStruct::from_json(&json).unwrap();
        assert_eq!(parsed.name, "test");
        assert_eq!(parsed.value, 42);
    }

    #[test]
    fn test_yaml_serializable() {
        let test_obj = TestStruct {
            name: "test".to_string(),
            value: 42,
        };
        
        let yaml = test_obj.to_yaml().unwrap();
        assert!(yaml.contains("test"));
        assert!(yaml.contains("42"));
        
        let parsed: TestStruct = TestStruct::from_yaml(&yaml).unwrap();
        assert_eq!(parsed.name, "test");
        assert_eq!(parsed.value, 42);
    }

    #[test]
    fn test_timestamp_helpers() {
        let now = now();
        let formatted = format_timestamp(&now);
        let parsed = parse_timestamp(&formatted).unwrap();
        
        // Allow for small differences due to precision
        let diff = (now - parsed).num_milliseconds().abs();
        assert!(diff < 1000); // Less than 1 second difference
    }
}
